<template>
	<view class="content">
		<view class="status-indicator" :class="connectionStatus ? 'connected' : 'disconnected'">
			<view class="status-dot"></view>
			<text>{{ connectionStatus ? '蓝牙已连接' : '蓝牙未连接' }}</text>
			<text v-if="lastChecked" class="status-time">最后检查: {{ lastChecked }}</text>
		</view>

		<button type="primary" @click="search_bluetooth">搜索蓝牙</button>
		<button type="primary" @click="handle_print">测试打印</button>
		<button type="warn" v-if="deviceId" @click="disconnect">断开连接</button>
		<button type="default" @click="checkConnectionNow">检查连接状态</button>

		<view class="status-bar" v-if="deviceId">
			<text>连接状态: {{ connectionStatus ? '已连接' : '已断开' }}</text>
			<text>当前设备: {{devices.find(d => d.deviceId === deviceId)?.name || deviceId}}</text>
		</view>
		<view class="bluetoothItem">
			<view class="bluetoothList" v-for="(item, index) in devices" :key="index" @tap="confirm_bluetooth(item)">
				<view class="bluetoothList-name">名称:{{ item.name }}</view>
				<view class="bluetoothList-mac">地址:{{ item.deviceId }}</view>
				<view class="bluetoothList-jange">------------</view>
			</view>
		</view>
	</view>
</template>

<script>
import { changeJson, openBlue, startBluetoothDevicesDiscovery, onfindBlueDevices, createBLEConnection, getBLEDeviceServices, getDeviceCharacteristics, sendDataToDevice } from '/cpcl/toCPCL'
import { template, printData } from '/cpcl/panels'
export default {
	data() {
		return {
			devices: [],
			deviceId: null,    // 蓝牙设备Id
			serviceId: null,   // 蓝牙服务Id
			writeId: null,     // 蓝牙特征写id
			readId: null,      // 蓝牙特征读Id
			isPrinting: false, // 记录是否正在打印中
			connectionStatus: false, // 蓝牙连接状态
			lastChecked: '', // 最后检查蓝牙连接状态时间
		}
	},
	onLoad() {
		// 1. 初始化蓝牙
		this.initBluetooth();

		// 1. 1使用低功耗蓝牙通讯 - 保持检查蓝牙连接状态，必須启动notifyBLECharacteristicValueChange
		try {
			uni.onBLEConnectionStateChange((res) => {
				if (res.deviceId === this.deviceId) {
					this.updateConnectionStatus(res.connected);
					if (!res.connected) {
						uni.showToast({
							title: '蓝牙连接已断开',
							icon: 'none'
						});
						this.isPrinting = false; // 重置打印状态
					}
				}
			});
		} catch (err) {
			console.error('监听低功耗蓝牙通讯特征值变化失败！', err);
			uni.showToast({
				title: '监听低功耗蓝牙通讯特征值变化失败！',
				icon: 'error'
			});
		}
	},

	methods: {
		// 更新连接状态并记录检查时间
		updateConnectionStatus(isConnected) {
			this.connectionStatus = isConnected;
			// 更新最后检查时间
			this.lastChecked = new Date().toLocaleTimeString('zh-CN', { hour12: false });
		},

		/**
		 * 手动检查连接状态
		 */
		async checkConnectionNow() {
			if (!this.deviceId) {
				this.updateConnectionStatus(false);
				uni.showToast({
					title: '未连接设备',
					icon: 'none'
				});
				return;
			}

			try {
				uni.showLoading({
					title: '检查连接状态...'
				});

				const connected = await this.checkBluetoothConnection();

				uni.hideLoading();
				uni.showToast({
					title: connected ? '设备已连接' : '设备已断开',
					icon: connected ? 'success' : 'none'
				});
			} catch (err) {
				uni.hideLoading();
				console.error('检查连接状态出错:', err);
				this.updateConnectionStatus(false);
			}
		},

		/**
		 * 1. 初始化蓝牙
		 */
		initBluetooth() {
			openBlue().then(() => {
				console.log('蓝牙初始化成功');
			}).catch(err => {
				console.error('蓝牙初始化失败', err);
				// 尝试重新初始化
				setTimeout(() => {
					this.initBluetooth();
				}, 3000);
			});
		},

		/**
		 * 2. 开始搜索蓝牙设备
		 */
		async search_bluetooth() {
			try {
				// 清空设备列表
				this.devices = [];
				// 开始搜索蓝牙设备
				await startBluetoothDevicesDiscovery()
				// 监听蓝牙设备发现事件
				onfindBlueDevices((devices) => {
					// 过滤可能的重复设备
					if (Array.isArray(devices)) {
						devices.forEach(device => {
							// 检查设备是否已在列表中
							const exists = this.devices.find(d => d.deviceId === device.deviceId);
							if (!exists && device.name) { // 只添加有名称的设备
								this.devices.push(device);
							}
						});
					}
				})
			} catch (error) {
				uni.showToast({
					title: '蓝牙连接错误',
					icon: 'none'
				});
			}
		},

		// 检查蓝牙连接状态 - 实际检查
		checkBluetoothConnection() {
			return new Promise((resolve) => {
				if (!this.deviceId) {
					resolve(false);
					return;
				}

				// uni.getBLEConnectStatus不存在，使用替代方法
				// 尝试获取设备服务作为连接状态检查
				uni.getBLEDeviceServices({
					deviceId: this.deviceId,
					success: (res) => {
						console.log('获取服务成功，设备已连接', res);
						this.connectionStatus = true;
						resolve(true);
					},
					fail: (err) => {
						console.error('获取服务失败，设备可能已断开', err);
						this.connectionStatus = false;
						resolve(false);
					}
				});
			});
		},

		// 在打印前检查连接状态，必要时尝试重连
		async ensureConnection() {
			const isConnected = await this.checkBluetoothConnection();
			if (!isConnected && this.deviceId) {
				// 尝试重连
				return new Promise((resolve) => {
					uni.showLoading({ title: '重新连接打印机...' });

					// 超时处理
					let isResolved = false;
					const timeoutId = setTimeout(() => {
						if (!isResolved) {
							isResolved = true;
							uni.hideLoading();
							uni.showToast({
								title: '连接超时，请重试',
								icon: 'none'
							});
							resolve(false);
						}
					}, 6000); // 6秒超时

					try {
						createBLEConnection(this.deviceId,
							(res) => {
								clearTimeout(timeoutId);
								if (isResolved) return;
								isResolved = true;

								console.log('重连成功', res);
								uni.hideLoading();
								this.connectionStatus = true;
								resolve(true);
							},
							(error) => {
								clearTimeout(timeoutId);
								if (isResolved) return;
								isResolved = true;

								console.error('重连失败', error);
								uni.hideLoading();
								uni.showToast({
									title: '打印机连接已断开，请重新连接',
									icon: 'none'
								});
								resolve(false);
							}
						);
					} catch (err) {
						clearTimeout(timeoutId);
						if (isResolved) return;
						isResolved = true;

						console.error('重连出错', err);
						uni.hideLoading();
						uni.showToast({
							title: '连接出错，请重试',
							icon: 'none'
						});
						resolve(false);
					}
				});
			}
			return isConnected;
		},

		/**
		 * 3. 连接蓝牙 - 点击连接蓝牙
		 * @param param0 蓝牙设备Id
		 */
		confirm_bluetooth({ deviceId }) {
			// 如果已经连接，先断开
			if (this.deviceId) {
				uni.closeBLEConnection({
					deviceId: this.deviceId,
					complete: () => {
						// 断开旧连接后连接新设备
						this.connectDevice(deviceId);
					}
				});
			} else {
				// 直接连接新设备
				this.connectDevice(deviceId);
			}
		},

		/**
		 * 3. 连接蓝牙设备 - 实际连接
		 * @param deviceId 
		 */
		connectDevice(deviceId) {
			uni.showLoading({ title: '连接中...' });

			// 保存当前设备ID
			this.deviceId = deviceId;

			// 3.1 开始连接蓝牙
			createBLEConnection(deviceId, (res) => {
				console.log('连接成功', res)
				this.connectionStatus = true;

				// 3.2 获取设备所有服务
				getBLEDeviceServices(deviceId, (services) => {
					console.log('获取服务成功', services)

					// 3.3 获取特征值
					getDeviceCharacteristics(deviceId, services.serviceId, (characteristics) => {
						console.log('获取特征值成功', characteristics)
						// 保存可写的特征值，用于发送数据
						this.serviceId = characteristics.serviceId;
						this.writeId = characteristics.writeId;
						this.readId = characteristics.readId;

						// 设置监听，用于接收打印机反馈
						if (characteristics.readId) {
							this.setupDeviceListener(deviceId, characteristics.serviceId, characteristics.readId);
						}

						uni.hideLoading();
						uni.showToast({
							title: '设备连接成功',
							icon: 'success',
							duration: 2000
						});
					},
						// 失败回调
						(error) => {
							console.error('获取特征值失败', error)
							uni.hideLoading();
							uni.showToast({
								title: '获取特征值失败',
								icon: 'none'
							});
						})
				},
					// 失败回调
					(error) => {
						console.error('获取服务失败', error)
						uni.hideLoading();
						uni.showToast({
							title: '获取服务失败',
							icon: 'none'
						});
					})
			},
				// 失败回调
				(error) => {
					console.error('连接失败', error)
					uni.hideLoading();
					uni.showToast({
						title: '连接失败',
						icon: 'none'
					});
				})
		},


		// 启用低功耗蓝牙设备特征值变化时的 notify 功能，订阅特征值
		setupDeviceListener(deviceId, serviceId, characteristicId) {
			try {
				// 启用notify
				uni.notifyBLECharacteristicValueChange({
					deviceId: deviceId,
					serviceId: serviceId,
					characteristicId: characteristicId,
					state: true,
					success: (res) => {
						console.log('启用notify成功', res);
						// 监听特征值变化
						uni.onBLECharacteristicValueChange((res) => {
							console.log('接收到设备数据', res);
							// 解析接收到的数据
							const value = new Uint8Array(res.value);
							let hexStr = '';
							for (let i = 0; i < value.length; i++) {
								hexStr += String.fromCharCode(value[i]);
							}
							console.log('设备返回数据:', hexStr);
						});
					},
					fail: (err) => {
						console.error('启用notify失败', err);
					}
				});
			} catch (err) {
				console.error('设置监听出错', err);
			}
		},


		/**
		 * 4. 断开连接 - 并清空蓝牙连接状态
		 */
		disconnect() {
			if (this.deviceId) {
				uni.closeBLEConnection({
					deviceId: this.deviceId,
					success: () => {
						console.log('断开连接成功');
						this.deviceId = null;
						this.serviceId = null;
						this.writeId = null;
						this.readId = null;
						this.connectionStatus = false;
						uni.showToast({
							title: '已断开连接',
							icon: 'success'
						});
					},
					fail: (err) => {
						console.error('断开连接失败', err);
					}
				});
			}
		},

		/**
		 * 5. 开始打印条码 - 检查打印机状态 - 设置打印机状态
		 */
		async handle_print() {

			// 5.1 检查是否已连接设备
			if (!this.deviceId || !this.serviceId || !this.writeId) {
				uni.showToast({
					title: '请先连接蓝牙设备',
					icon: 'none'
				})
				return
			}

			// 5.2 检查蓝牙并确保连接正常
			const connected = await this.ensureConnection();
			if (!connected) {
				return; // 连接失败，中止打印
			}

			// 5.3 检查是否正在打印中...
			if (this.isPrinting) {
				uni.showToast({
					title: '正在打印中，请稍后再试',
					icon: 'none'
				});
				return;
			}

			// 5.4 设置打印状态
			this.isPrinting = true;

			// 5.5 开始蓝牙传输打印数据
			uni.hideLoading();
			this.proceedWithPrinting();
		},

		/**
		 *  6. 开始执行实际打印步骤
		 */
		proceedWithPrinting() {

			// 6.1 将JSON模板和数据转换为CPCL指令并获取二进制数据
			console.log('开始转换CPCL指令...');
			const buffer = changeJson(template.panels[0], printData[0])
			console.log('转换完成，准备发送数据...');
			console.log('数据长度:', buffer.byteLength);

			// 设置分片大小，确保一次发送的数据不会太大
			const onceLength = 20; // 蓝牙推荐每次发送20字节

			// 发送数据到打印机
			console.log('准备发送数据到打印机...');

			try {
				// 显示打印进度提示
				uni.showLoading({ title: '打印数据发送中...' });

				// 估算总传输时间
				const totalSize = buffer.byteLength;
				console.log(`总数据大小: ${totalSize}字节`);

				// 6.2 监控传输进度
				let sentBytes = 0;
				const progressCallback = (sent) => {
					sentBytes += sent;
					const progress = Math.min(99, Math.floor((sentBytes / totalSize) * 100));
					uni.showLoading({
						title: `正在发送数据 ${progress}%`
					});
				};

				// 6.3 实际传输数据给打印机
				sendDataToDevice({
					deviceId: this.deviceId,
					serviceId: this.serviceId,
					characteristicId: this.writeId,
					value: buffer,
					onceLength: onceLength, // 添加分片大小
					progressCallback, // 传输进度回调

					// 6.4 打印完成回调
					lasterSuccess: () => {
						uni.hideLoading();
						uni.showLoading({ title: '正在打印中...' });

						// 部分打印机可能需要额外的打印命令或结束命令
						// 使用结束命令避免导致额外出纸
						this.sendSimpleEndCommand();

						// 打印完成后隐藏加载提示 - 根据打印内容调整此时间
						setTimeout(() => {
							uni.hideLoading();
							uni.showToast({ title: '打印完成', icon: 'success', duration: 2000 });
							this.isPrinting = false; // 重置打印状态
						}, 2000);
					},
					success: (res) => {
						console.log('单个数据包发送成功！', res)
					},
					fail: (error) => {
						console.error('打印失败', error)
						this.isPrinting = false; // 重置打印状态
						uni.hideLoading();
						uni.showToast({ title: '打印失败', icon: 'none' })
					}
				})
			} catch (err) {
				console.error('发送数据过程中发生错误:', err);
				this.isPrinting = false; // 重置打印状态
				uni.hideLoading();
				uni.showToast({ title: '发送数据错误!', icon: 'none' });
			}
		},


		// 7. 打印结束-执行一些刷新命令
		sendSimpleEndCommand() {
			console.log('发送打印结束命令');

			// 更新加载状态
			uni.showLoading({ title: '完成打印...' });

			try {
				// 标签纸的结束命令
				const labelEndCommands = [
					// 结束当前打印命令
					{ name: '结束当前打印', cmd: this.strToArrayBuffer('\r\n') },
					// 确保打印内容已完全传输
					{ name: '打印刷新', cmd: this.strToArrayBuffer('! U1 PRINT\r\n') },
					// 标签进给，移动到下一张标签开始位置
					{ name: '标签进给', cmd: this.strToArrayBuffer('! U1 FORM\r\n') },
					// 强制标签纸重新校准定位
					{ name: '标签重新校准', cmd: this.strToArrayBuffer('! U1 GAP-SENSE\r\n') }
				];

				// 发送标签结束命令序列，增加延迟确保每条命令正确执行
				this.sendCommandsSequentially(labelEndCommands, 0, 500, () => {
					console.log('标签打印完成并重新定位');
				});
			} catch (err) {
				console.error('发送结束命令错误', err);
			}
		},

		// 8. 依次发送命令
		sendCommandsSequentially(commands, index, delay, finalCallback) {
			if (index >= commands.length) {
				console.log('命令序列发送完成');
				finalCallback && finalCallback();
				return;
			}

			const cmd = commands[index];
			console.log(`发送命令: ${cmd.name}`);

			uni.writeBLECharacteristicValue({
				deviceId: this.deviceId,
				serviceId: this.serviceId,
				characteristicId: this.writeId,
				value: cmd.cmd,
				success: () => {
					console.log(`${cmd.name}命令发送成功`);
				},
				fail: (err) => {
					console.error(`${cmd.name}命令发送失败`, err);
				},
				complete: () => {
					// 继续发送下一个命令
					setTimeout(() => {
						this.sendCommandsSequentially(commands, index + 1, delay, finalCallback);
					}, delay);
				}
			});
		},

		// 9. 字符串转ArrayBuffer
		strToArrayBuffer(str) {
			const buffer = new ArrayBuffer(str.length);
			const view = new Uint8Array(buffer);
			for (let i = 0; i < str.length; i++) {
				view[i] = str.charCodeAt(i);
			}
			return buffer;
		},

		onUnload() {
			if (this.deviceId) { // 断开当前连接
				this.disconnect();
			}
		},
	}
}
</script>

<style>
.content {
	display: flex;
	flex-direction: column;
	gap: 20rpx;
}

.status-bar {
	display: flex;
	flex-direction: column;
	padding: 20rpx;
	background-color: #f8f8f8;
	border-radius: 10rpx;
	margin: 10rpx 0;
	border: 1px solid #eee;
}

.status-bar text {
	font-size: 28rpx;
	margin-bottom: 10rpx;
}

.bluetoothItem {
	max-height: 800rpx;
	overflow-y: auto;
}

.bluetoothList {
	padding: 20rpx;
	border-bottom: 1px solid #eee;
	background-color: #fff;
	border-radius: 10rpx;
	margin-bottom: 10rpx;
}

.bluetoothList-name {
	font-weight: bold;
	font-size: 30rpx;
}

.bluetoothList-mac {
	font-size: 24rpx;
	color: #666;
	margin-top: 10rpx;
}

.status-indicator {
	display: flex;
	align-items: center;
	padding: 10rpx;
	background-color: #f8f8f8;
	border-radius: 10rpx;
	margin: 10rpx 0;
	border: 1px solid #eee;
}

.status-dot {
	width: 20rpx;
	height: 20rpx;
	border-radius: 50%;
	background-color: #000;
	margin-right: 10rpx;
}

.connected .status-dot {
	background-color: #0f0;
}

.disconnected .status-dot {
	background-color: #f00;
}

.status-time {
	font-size: 24rpx;
	color: #666;
	margin-left: 10rpx;
}
</style>
